﻿using System;
using System.Linq;
using System.Reflection;
#if UNITY
using UnityEngine;
#endif
#if ODIN_INSPECTOR
using Sirenix.OdinInspector;
#endif
#if USING_XLUA
using XLua;
#endif
// ReSharper disable once CheckNamespace
namespace com.susi.util.toolkit {
    public interface IMessage { }

    public interface IListener {
        void OnMessage<T>(T msg) where T :  IMessage;
    }

    public interface IEventSource : IDisposaEventer,IDisposaEventer<IEventSource>
    {
        /// <summary>
        ///添加特定事件监听器 
        /// </summary>
        void Add<T>(Action<T> onMsg) where T :  IMessage;

        /// <summary>
        ///添加所有事件监听器 
        /// </summary>
        void Add(IListener listener);
#if USING_XLUA
        /// <summary>
        /// 添加特定事件监听器
        /// <para>会尝试调用table:{msgType}(msg)</para>
        /// </summary>
        void Add(string msgType, LuaTable table);

        /// <summary>
        /// 添加所有事件监听器
        /// <para>会尝试调用table:onMessage（msg)</para>
        /// </summary>
        void Add(LuaTable table);
#endif
        /// <summary>
        ///移除特定事件监听器 
        /// </summary>
        void Remove<T>(Action<T> onMsg) where T :  IMessage;

        /// <summary>
        ///移除所有事件监听器 
        /// </summary>
        void Remove(IListener listener);
#if USING_XLUA
        /// <summary>
        /// 移除特定事件监听器(主要给lua用，c#部分用<see cref="Add{T}"/>)
        /// </summary>
        void Remove(string msgType, LuaTable table);

        /// <summary>
        ///移除所有事件监听器 
        /// </summary>
        void Remove(LuaTable table);
#endif
    }

    public class EmptyEventSource : IEventSource {
        public static EmptyEventSource Instance {
            get {
                if (_Instance == null) _Instance = new EmptyEventSource();
                return _Instance;
            }
        }

        private static EmptyEventSource _Instance;

        /// <summary>
        /// 监听此事件会立即触发事件
        /// </summary>
        public event Action onDisposingEvent {
            add { value?.Invoke(); }
            remove { }
        }

        /// <summary>
        /// 监听此事件会立即触发事件
        /// </summary>
        public event Action<IEventSource> onDisposingTEvent {
            add { value?.Invoke(this); }
            remove { }
        }

        public void Add<T>(Action<T> onMsg) where T :  IMessage { }

        public void Add(IListener listener) { }

        public void Remove<T>(Action<T> onMsg) where T :  IMessage { }

        public void Remove(IListener listener) { }
#if USING_XLUA
        public void Add(string msgType, LuaTable table) { }

        public void Add(LuaTable table) { }

        public void Remove(string msgType, LuaTable table) { }

        public void Remove(LuaTable table) { }
#endif

        public void Dispose() { }
    }

    public static class IEventSourceExtension {
        public static void RemoveAndAdd<T>(this IEventSource eventSource, Action<T> ac) where T : struct, IMessage
        {
            eventSource.Remove(ac);
            eventSource.Add(ac);
        }

        public static void RemoveAndAdd(this IEventSource eventSource, IListener lit)
        {
            eventSource.Remove(lit);
            eventSource.Add(lit);
        }
#if USING_XLUA
        public static void RemoveAndAdd<T>(this IEventSource eventSource, string msg, LuaTable table) where T : struct, IMessage
        {
            eventSource.Remove(msg, table);
            eventSource.Add(msg, table);
        }

        public static void RemoveAndAdd(this IEventSource eventSource, LuaTable table)
        {
            eventSource.Remove(table);
            eventSource.Add(table);
        }
#endif
    }

    public static class IListenerExtension {
        /// <summary>
        /// 通过反射拓展的功能，仅用于非泛型调用
        /// </summary>
#if ODIN_INSPECTOR
        [Button("发送消息(必须保证消息填写正确)")]
#endif
        public static void OnMessageByIns(this IListener lit, IMessage message)
        {
            if (message == null) return;
            var type = message.GetType();
            var tar = (
                          from method in lit.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance)
                          where method.Name == nameof(OnMessageByIns)
                          let t = method.GetGenericArguments()
                          where t != null && t.Length == 1
                          select method).FirstOrDefault();
            if (tar == null) {
#if UNITY
                Debug.LogError("没能找到消息");
#endif
                return;
            }

            // Debug.Log(tar.GetFullName()); //ok
            var m = tar.MakeGenericMethod(new[] {type});
            // Debug.Log(m.GetFullName());
            m.Invoke(lit, new object[] {message});
        }
    }
}